#include "PlaneMeshlight.cuh"

using namespace OBR;

__forceinline__ __device__ Spectrum calcLi(RayData &ray, float *pdf, PlaneMeshLightData *data)
{
    float costheta = -cos(ray.d, data->normal_world);
    if (costheta > 0)
    {
        float r2 = ray.t * ray.t;
        *pdf = (1 / data->area) * (r2 / costheta);
        return data->radiance;
    }
    else
    {
        *pdf = 0;
        return Spectrum(0.0f);
    }
}

__forceinline__ __device__ Spectrum sampleLi(SamplePRD *prd, float *pdf, float u0, float u1,
                                             PlaneMeshLightData *data)
{
    float3 p = data->corner + data->U * u0 + data->V * u1;
    p = data->l2w * p;
    prd->inter.wi = prd->inter.spawnRayTo(p);
    return calcLi(prd->inter.wi, pdf, data);
}

extern "C" __device__ Spectrum __direct_callable__rectangle_light(SamplePRD *prd, float *pdf,
                                                                  int instance_id, float u0,
                                                                  float u1,

                                                                  LightCallMode mode)
{
#ifdef DEBUG_SHADER
    if (prd->log)
    {
        printf("==== plane mesh light program launched\n");
    }
#endif

    CallableData *sbt_record = reinterpret_cast<CallableData *>(optixGetSbtDataPointer());
    if (instance_id < sbt_record->n_instances)
    {
        PlaneMeshLightData *data =
            reinterpret_cast<PlaneMeshLightData *>(sbt_record->callable_payload) + instance_id;
        if (mode == LightCallMode::SAMPLE_LI)
        {
            return sampleLi(prd, pdf, u0, u1, data);
        }
        else if (mode == LightCallMode::CALC_LI_PDF)
        {
            return calcLi(prd->inter.wo, pdf, data);
        }
    }
    return Spectrum(0.0f);
}